package com.atguigu.exam.service.impl;
import com.atguigu.exam.entity.Category;
import com.atguigu.exam.entity.Question;
import com.atguigu.exam.exception.ExamException;
import com.atguigu.exam.mapper.CategoryMapper;
import com.atguigu.exam.mapper.QuestionMapper;
import com.atguigu.exam.service.CategoryService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.LongStream;


@Slf4j
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {


    @Autowired
    private QuestionMapper questionMapper;

    @Override
    public List<Category> getCategories() {
        //获取所有分类列表,按sort字段排序
        List<Category> categories = baseMapper.selectList(new LambdaQueryWrapper<Category>().orderByAsc(Category::getSort));
        getQuestionCount( categories);
        return categories;
    }

    @Override
    public List getCategoryTree() {
        //获取所有的分类列表，按照sort字段排序
        List categories = baseMapper.selectList(new LambdaQueryWrapper<Category>().orderByAsc(Category::getSort));
        //获取每个分类下的题目数量
        getQuestionCount(categories);
        //构建分类树
        List<Category> categoryTree = buildCategoryTree(categories);

        return categoryTree;
    }

    @Override
    public void addCategory(Category category) {
        //根据父类id判断父类下该名称是否已经存在
        //先创建一个LambdaQueryWrapper
        //
        /*
        categoryLambdaQueryWrapper.eq(Category::getParentId,category.getParentId())
            eq方法表示等于(=)条件
            Category::getParentId是方法引用，指向Category实体类的parentId字段
            category.getParentId()获取传入参数category对象的父分类ID值
            这部分构建了查询条件：parent_id = ?（参数为当前分类的父ID）
        .eq(Category::getName,category.getName())
            同样使用eq方法构建等于条件
            Category::getName指向Category实体类的name字段
            category.getName()获取传入参数category对象的名称
            这部分构建了查询条件：name = ?（参数为当前分类的名称）
            等价于:
            WHERE parent_id = ? AND name = ?
        */
        LambdaQueryWrapper<Category> categoryLambdaQueryWrapper = new LambdaQueryWrapper<Category>().eq(Category::getParentId, category.getParentId()).eq(Category::getName, category.getName());
        //调用带条件查询的方法
        Category isExistCategory = baseMapper.selectOne(categoryLambdaQueryWrapper);
        if(isExistCategory != null){
            //获取父id
            Long parentId = category.getParentId();
            //根据父id查询,可得到父name
            Category parentCategory = baseMapper.selectById(parentId);
            //抛出异常
            throw new ExamException(111,"父分类<"+parentCategory.getName()+">下已存在<"+isExistCategory.getName()+">分类，请重新输入分类名称");
        }

        baseMapper.insert(category);
    }

    @Override
    public void updateCategory(Category category) {
        //根据当前分类父id查询所有子分类
        List<Category> childcategories = baseMapper.selectList(new LambdaQueryWrapper<Category>().eq(Category::getParentId, category.getParentId()));
        //获取所有子分类名字
        List<String> childCategoryNames = childcategories.stream().map(childCategory -> childCategory.getName()).collect(Collectors.toList());
        //判断当前分类父id下是否已存在同名分类
        if (childCategoryNames.contains(category.getName())) {
            //判断更新的名称是否与当前分类名称一致
            Category oldCategoryName = baseMapper.selectOne(new LambdaQueryWrapper<Category>().eq(Category::getName, category.getName()));
            if (oldCategoryName.getId().equals(category.getId())) {
                //证明是自己，没有修改名称，可能是在修改其他字段，可以更新
                baseMapper.updateById(category);
            }else {
                //获取父id
                Long parentId = category.getParentId();
                //根据父id查询,可得到父name
                Category parentCategory = baseMapper.selectById(parentId);
                //名称不一致，不能更新
                throw new ExamException(111,"父分类<"+oldCategoryName.getName()+">下已存在<"+category.getName()+">分类，请重新输入分类名称");
            }


        }
        //更新
        baseMapper.updateById(category);
    }

    @Override
    public void removeCategory(Long id) {
        //该分类下没有子分类,或者该分类下没有题目可以删除
        //baseMapper：用于对分类(Category)表进行增删改查操作
        //questionMapper：用于对题目(Question)表进行增删改查操作
        List<Category> categories = baseMapper.selectList(new LambdaQueryWrapper<Category>().eq(Category::getParentId, id));
        Long questionCount = questionMapper.selectCount(new LambdaQueryWrapper<Question>().eq(Question::getCategoryId, id));
        if (categories.size() > 0 || questionCount > 0) {
            throw new ExamException(111,"该分类下有子分类或者题目，请先删除子分类和题目");
        }
        baseMapper.deleteById(id);

    }

    //获取每个分类下的题目数量的方法
    private void getQuestionCount(List<Category> categories) {
        // 获取每个分类下的题目数量,由于是键值对格式,用Map封装
        List<Map<Long, Long>> countList = questionMapper.getCategoryCount();
        //将List<Map<Long,Long>>转换为Map<Long,Long>,空间换时间降低时间复杂度
        Map<Long, Long> countMap = countList.stream().collect(Collectors.toMap(
                m -> m.get("category_id"),
                m -> m.get("count")
        ));

        //遍历所有的分类
        categories.forEach(category -> {
            //根据分类id从Map中获取题目数量
            Long questionCount = countMap.getOrDefault(category.getId(), 0L);
            //给分类设置题目数量
            category.setCount(questionCount);
        });
    }

    //构建分类树结构的方法
    private List<Category> buildCategoryTree(List<Category> categories){
        //按照parent_id进行分组,获取每个父级分类下的子分类
        /*
          语法解析:
          将 List<Category> 转换为流对象 Stream<Category>,用于支持函数式编程操作
          Collectors.groupingBy() 是一个收集器，用于按指定条件对元素进行分组
          Category::getParentId 是方法引用，等价于 Lambda 表达式 category -> category.getParentId()
        */
        Map<Long,List<Category>> parentIdMap = categories.stream().collect(Collectors.groupingBy(Category::getParentId));
        //遍历所有的分类
        categories.forEach(category -> {
            //获取当前分类的Id
            Long id = category.getId();
            //根据当前分类的Id从Map中获取子分类
            List<Category> childrenCategories = parentIdMap.getOrDefault(id, new ArrayList<>());
            //将子分类对象添加到当前分类对象中
            category.setChildren(childrenCategories);
            //获取所有子分类题目数量之和
            /*
                语法解析:
                mapToLong：将流中的每个元素转换为 long 类型的数值
                将流作为参数c传入,再调用getCount()方法获取题目数量,若不为空则返回
            */
            LongStream childrenSumQuestionCount= childrenCategories.stream().mapToLong(c -> c.getCount() != null ? c.getCount() : 0L);
            //获取当前分类题目数量
            Long count = category.getCount();
            //则前端显示的count等于当前分类题目数量+所有子分类题目数量之和
            category.setCount(count + childrenSumQuestionCount.sum());
        });
        //获取所有顶级分类,即parent_id为0的分类
        //转换成流,过滤出parent_id为0的分类,再收集成列表
        List<Category> collect = categories.stream().filter(c -> c.getParentId() == 0).collect(Collectors.toList());
        return collect;

    }
}